from django.db import transaction
from django.utils import timezone
from rest_framework import serializers
from django_redis import get_redis_connection
from decimal import Decimal
import logging

from goods.models import SKU
from .models import OrderInfo, OrderGoods

logger = logging.getLogger('django')


class CartSKUSerializer(serializers.ModelSerializer):
    """
    购物车商品序列化器
    """
    count = serializers.IntegerField(label='数量')

    class Meta:
        model = SKU
        fields = ('id', 'name', 'default_image_url', 'price', 'count')


class OrderSettlementSerializer(serializers.Serializer):
    """
    订单结算序列化器
    """
    freight = serializers.DecimalField(label='运费', max_digits=10, decimal_places=2)
    skus = CartSKUSerializer(many=True)


class SaveOrderSerializer(serializers.ModelSerializer):
    """
    保存订单数据序列化器
    """
    class Meta:
        model = OrderInfo
        fields = ('order_id', 'address', 'pay_method')
        read_only_fields = ('order_id',)
        extra_kwargs = {
            'address': {
                'write_only': True,
            },
            'pay_method': {
                'required': True
            }
        }

    def create(self, validated_data):
        """保存订单"""
        address = validated_data['address']
        pay_method = validated_data['pay_method']

        # 获取当前用户
        user = self.context['request'].user

        # 从redis中获取购物车结算商品的数据
        redis_conn = get_redis_connection('cart')
        sku_count = redis_conn.hgetall('cart_%s' % user.id)
        selected = redis_conn.smembers('cart_selected_%s' % user.id)

        # 转换类型
        cart = {}
        for sku_id in selected:
            cart[int(sku_id)] = int(sku_count[sku_id])

        if not cart:
            raise serializers.ValidationError('没有需要结算的商品')

        # 创建开启一个事务
        with transaction.atomic():
            try:
                # 创建保存点
                save_id = transaction.savepoint()

                # 生成订单编号
                # 格式：20190121193315 + 9位用户id
                order_id = timezone.now().strftime('%Y%m%d%H%m%S') + ('%09d' % user.id)

                # 创建OrderInfo对象
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal('0'),
                    freight=Decimal('10'),
                    pay_method=pay_method,
                    status=OrderInfo.ORDER_STATUS_ENUM['UNSEND'] if pay_method == OrderInfo.PAY_METHODS_ENUM['CASH'] else
                    OrderInfo.ORDER_STATUS_ENUM['UNPAID']
                )

                # 遍历结算商品
                for sku_id in cart:
                    while True:
                        # 从数据库中查询商品数据
                        sku = SKU.objects.get(id=sku_id)

                        # 原始的销量和库存，每次循环都有可能变化
                        origin_stock = sku.stock
                        origin_sales = sku.sales

                        count = cart[sku_id]
                        # 判断库存
                        if count > origin_stock:
                            # 回滚到保存点
                            transaction.savepoint_rollback(save_id)
                            raise serializers.ValidationError('%s库存不足' % sku.name)

                        # 修改库存和销量
                        new_stock = origin_stock - count
                        new_sales = origin_sales + count

                        # result返回值，为受影响的行数
                        result = SKU.objects.filter(id=sku.id, stock=origin_stock).update(stock=new_stock, sales=new_sales)
                        # 如果此时数据库中的库存还是原始的库存，就说明没有发生并发下单， 则对数据库进行修改，并往下执行（即result为1）
                        if result == 0:
                            continue

                        # 修改商品的SPU 销量信息
                        sku.goods.sales += count
                        sku.goods.save()

                        # 创建OrderGoods对象
                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=count,
                            price=sku.price
                        )

                        # 修改订单的数量总额和总金额
                        order.total_count += count
                        order.total_amount += (count * sku.price)

                        break

                # 增加订单的运费
                order.total_amount += order.freight
                order.save()

            except serializers.ValidationError:
                raise
            except Exception as e:
                logger.error(e)
                transaction.savepoint_rollback(save_id)
                raise
            else:
                # 提交从保存点到当前状态的所有数据库事务操作
                transaction.savepoint_commit(save_id)

        # 在redis购物车中删除已经计算的商品数据
        pl = redis_conn.pipeline()
        pl.hdel('cart_%s' % user.id, *selected)
        pl.srem('cart_selected_%s' % user.id, *selected)
        pl.execute()

        # 返回
        return order









